package business;

import dao.EmpregadoDAO;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import model.CPF;
import model.Empregado;

public class EmpregadoBO {
	
    private static EmpregadoBO instancia;

    /**
     * Método executado para garantir a existência de apenas uma instância dessa classe (Singleton).
     * 
     * @return uma instância da classe EmpregadoBO 
     * 
     */
    public static EmpregadoBO getInstance() {
        if (instancia == null) {
            instancia = new EmpregadoBO();
        }
        return instancia;
    }

    /**
     * Método executado para validar as informações de empregado antes de ser incluído.
     * 
     * @param Empregado a ser incluido
     * @throws Exception, EmpregadoException
     * 
     */
    public void incluiEmpregado(Empregado empregado) throws Exception, EmpregadoException {

        if (EmpregadoBO.getInstance().verificaCamposObrigatorios(empregado) &&
            EmpregadoBO.getInstance().validaNome(empregado.getNome()) &&
            EmpregadoBO.getInstance().validaSexo(empregado.getSexoPorExtenso()) && 
            EmpregadoBO.getInstance().validaSalario(empregado.getSalario()) &&
            EmpregadoBO.getInstance().validaCpf(empregado.getCpf())  &&
            EmpregadoBO.getInstance().validaDataAdmissao(empregado.getDataAdmissao(), empregado.getDataNascimento()) &&
            EmpregadoBO.getInstance().validaDataDesligamento(empregado.getDataDesligamento(), empregado.getDataAdmissao()) &&
            EmpregadoBO.getInstance().validaDataNascimento(empregado.getDataNascimento(), empregado.getDataAdmissao()) && 
            EmpregadoBO.getInstance().validaDatasComDataHoje(empregado.getDataNascimento(), empregado.getDataAdmissao(), empregado.getDataDesligamento())) {

            EmpregadoDAO.getInstance().incluir(empregado);

        }                   

    }

    /**
     * Método executado para validar as informações de empregado antes de ser alterado.
     * 
     * @param Empregado a ser incluido
     * @throws Exception, EmpregadoException
     * 
     */
    public void alteraEmpregado(Empregado antigoEmpregado, Empregado novoEmpregado) throws Exception, EmpregadoException {

        if (EmpregadoBO.getInstance().verificaCamposObrigatorios(antigoEmpregado) &&
            EmpregadoBO.getInstance().validaNome(antigoEmpregado.getNome()) &&
            EmpregadoBO.getInstance().validaSexo(antigoEmpregado.getSexoPorExtenso()) && 
            EmpregadoBO.getInstance().validaSalario(antigoEmpregado.getSalario()) &&
            EmpregadoBO.getInstance().validaCpf(antigoEmpregado.getCpf())  &&
            EmpregadoBO.getInstance().validaDataAdmissao(antigoEmpregado.getDataAdmissao(), antigoEmpregado.getDataNascimento()) &&
            EmpregadoBO.getInstance().validaDataDesligamento(antigoEmpregado.getDataDesligamento(), antigoEmpregado.getDataAdmissao()) &&
            EmpregadoBO.getInstance().validaDataNascimento(antigoEmpregado.getDataNascimento(), antigoEmpregado.getDataAdmissao()) && 
            EmpregadoBO.getInstance().validaDatasComDataHoje(antigoEmpregado.getDataNascimento(), antigoEmpregado.getDataAdmissao(), antigoEmpregado.getDataDesligamento())) {

            EmpregadoDAO.getInstance().alterar(antigoEmpregado, novoEmpregado);

        }                   

    }

    /**
     * Método executado para verificar se o nome do empregado possui no máximo 100 caracteres.
     * 
     * @param Nome do empregado a ser incluido
     * @return Booleano que indica se o nome é válido ou não
     * 
     */
    public boolean validaNome(String nome) throws EmpregadoException {

        if (nome.length() <= 100) {
            return true;
        }

        throw new EmpregadoException("Não é permitido nomes com mais de 100 caracteres");

    }

    /**
     * Método executado para verificar se o valor do salário do empregado está entre 500.00 e 100000.00.
     * 
     * @param Valor do salário
     * @return Booleano que indica se o salário é válido ou não
     * 
     */
    public boolean validaSalario(BigDecimal salario) throws EmpregadoException {

        BigDecimal limiteInferior = new BigDecimal(500.00).setScale(2, RoundingMode.HALF_EVEN);
        BigDecimal limiteSuperior = new BigDecimal(100000.00).setScale(2, RoundingMode.HALF_EVEN);

        if (salario.compareTo(limiteInferior) >= 0 && salario.compareTo(limiteSuperior) <= 0) {
            return true;
        }

        throw new EmpregadoException("Salário inválido: entre com um valor entre 500.00 e 100000.00");

    }

    /**
     * Método executado para verificar se o sexo do empregado é masculino ou feminino.
     * 
     * @param Sexo do empregado
     * @return Booleano que indica se o sexo é válido ou não
     * 
     */
    public boolean validaSexo(String sexo) throws EmpregadoException {

        if (sexo.equals("Masculino") || sexo.equals("Feminino")) {
            return true;
        }

        throw new EmpregadoException("O sexo só pode ser feminino ou masculino.");

    }

    /**
     * Método executado para verificar se os campos obrigatórios estão preenchidos.
     * São campos obrigatórios: nome, sexo, salário atual, CPF, data de nascimento e data de admissão.
     *
     * @param empregado do tipo Empregado
     * @return Booleano que indica se estão preenchidos ou não
     *
     */
    public boolean verificaCamposObrigatorios(Empregado empregado) throws EmpregadoException
    {
        if(!empregado.getNome().equals("") && empregado.getSexo() != null && empregado.getSalario() != null && !empregado.getCpf().equals("") && !empregado.getDataNascimento().equals("") && !empregado.getDataAdmissao().equals("")) {
            return true; 
        }

        throw new EmpregadoException("Os campos obrigatórios não foram preenchidos.");

    }

   /**
    * Método que verifica se o CPF inserido é um CPF válido.
    * 
    * @param CPF do empregado
    * @return Booleano que indica se o cpf é válido ou não
    * 
    */
    public  boolean validaCpf(String cpf) throws EmpregadoException {
        if (CPF.validaCPF(cpf)) {
            return true;
        }

        throw new EmpregadoException("Entre com um CPF válido.");

    }  

    /**
    * Método que verifica se o CPF inserido é um CPF válido.
    * 
    * @param CPF do empregado a ser inserido
    * @return Booleano que indica se esse CPF já está cadastrado
    * 
    */
    public  boolean validaExistenciaCpf(String cpf) throws EmpregadoException, Exception {

        for (int i = 0; i < EmpregadoDAO.getInstance().listar().size(); i++) {
            if (EmpregadoDAO.getInstance().listar().get(i).getCpf().equals(cpf)) {
                throw new EmpregadoException("Esse CPF já está cadastrado.");
            }
        }

        return true;

    }

    /**
    * Método que verifica se a data de admissão é posterior à data de nascimento.
    * 
    * @param Data de admissão e nascimento do empregado
    * @return Booleano que indica se a data de admissão é posterior
    * 
    */

    public  boolean validaDataAdmissao(String dataAdmissao, String dataNascimento) throws EmpregadoException, ParseException {

        SimpleDateFormat formatar = new SimpleDateFormat("dd/MM/yyyy");

        Date dataAdm = formatar.parse(dataAdmissao);
        Date dataNasc = formatar.parse(dataNascimento);

        if (dataAdm.compareTo(dataNasc) == 1) {
               return true;
        }

        throw new EmpregadoException("A data de admissão deve ser posterior à data de nascimento.");

    } 

    /**
    * Método que verifica se a data de desligamento é posterior à data de admissão .
    * 
    * @param Data de desligamento e admissão do empregado
    * @return Booleano que indica se a data de desligamento é posterior
    * 
    */
    public  boolean validaDataDesligamento(String dataDesligamento, String dataAdmissao) throws EmpregadoException, ParseException {

        SimpleDateFormat formatar = new SimpleDateFormat("dd/MM/yyyy");

        if (!dataDesligamento.equals("")) {
            Date dataDesl = formatar.parse(dataDesligamento);
            Date dataAdm = formatar.parse(dataAdmissao);

            if (dataDesl.compareTo(dataAdm) == 1) {
                   return true;
            }

            throw new EmpregadoException("A data de desligamento deve ser posterior à data de admissão.");
        }
        else {
            return true;
        }

    }

    /**
    * Método que verifica se o empregado não é menor de 18 anos.
    * 
    * @param Data de nascimento e admissão do empregado
    * @return Booleano que indica se a data de admissão é posterior
    * 
    */
    public  boolean validaDataNascimento(String dataNascimento, String dataAdmissao) throws EmpregadoException, ParseException {

        SimpleDateFormat formatar = new SimpleDateFormat("dd/MM/yyyy");

        Date dataNasc = formatar.parse(dataNascimento);
        Date dataAdm = formatar.parse(dataAdmissao);

        String anoAdmissao = dataAdm.toString();
        anoAdmissao = anoAdmissao.substring(anoAdmissao.length() - 4, anoAdmissao.length());

        Calendar dezoitoAnosAtrasAdmissao = Calendar.getInstance();

        dezoitoAnosAtrasAdmissao.set(Calendar.YEAR, Integer.parseInt(anoAdmissao));
        dezoitoAnosAtrasAdmissao.add(Calendar.YEAR, -18); 

        if (dataNasc.compareTo(dezoitoAnosAtrasAdmissao.getTime()) == -1) {
           return true;
        }

        throw new EmpregadoException("A empresa não contrata menores de 18 anos.");

    }

    /**
    * Método que verifica se a data de nascimento, a data de admissão 
    * e a data de desligamento não são posteriores à data de hoje.
    * 
    * @param Data de nascimento, admissão e desligamento do empregado
    * @return Booleano que indica se essas datas são posteriores
    * 
    */
    public  boolean validaDatasComDataHoje(String dataNascimento, String dataAdmissao, String dataDesligamento) throws EmpregadoException, ParseException {

        SimpleDateFormat formatar = new SimpleDateFormat("dd/MM/yyyy");

        Date dataNasc = formatar.parse(dataNascimento);
        Date dataAdm = formatar.parse(dataAdmissao);
        Date dataDesl;

        if (!dataDesligamento.equals("")) {
            dataDesl= formatar.parse(dataDesligamento);
        }
        else {
            dataDesl = dataAdm;
        }

        Calendar hoje = Calendar.getInstance();

        if (dataNasc.compareTo(hoje.getTime()) == -1 && dataAdm.compareTo(hoje.getTime()) == -1 && dataDesl.compareTo(hoje.getTime()) == -1) {
               return true;
        }

        throw new EmpregadoException("As datas de nascimento, admissão e desligamento devem ser anteriores à data de hoje.");

    }

    /**
    * Método calcula a idade da pessoa na data atual (dia de hoje)
    * 
    * @param Data de nascimento
    * @return inteiro com a idade
    * 
    */
    public int calculaIdade(String dataNascimento) throws EmpregadoException, ParseException
    {
        int idade = 0;
        SimpleDateFormat formatar = new SimpleDateFormat("dd/MM/yyyy");

        Date dataNasc = formatar.parse(dataNascimento);
        Calendar hoje = Calendar.getInstance();

        Date dataNascVerificacao = (Date)dataNasc.clone();

        dataNascVerificacao.setYear(hoje.getTime().getYear());

        if (dataNascVerificacao.compareTo(hoje.getTime()) <= 0) { 
            idade = hoje.getTime().getYear() - dataNasc.getYear();
        }
        else {
            idade = (hoje.getTime().getYear() - dataNasc.getYear()) - 1;
        }

        return idade;
    }

}
